//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#pragma once

#include "Graphics/Resources/Texture.h"
#include "Graphics/Resources/Program.h"
#include "Graphics/GLshell/Buffer.h"

namespace Engine
{
namespace Graphics
{

	//
	// Batch Material
	//

	struct BatchMaterial
	{
		friend class BatchRender;

	// types
	public:
		typedef TOptional< Sampler >	SamplerOptional_t;

	private:
		struct BatchTexture
		{
			TexturePtr			texture;
			SamplerOptional_t	sampler;
			///
			BatchTexture () {}
			BatchTexture (const TexturePtr &tex) : texture(tex) {}
			BatchTexture (const TexturePtr &tex, const Sampler &samp) : texture(tex), sampler(samp) {}

			bool operator == (const BatchTexture &right) const
			{
				return	texture == right.texture and
						sampler.IsDefined() == right.sampler.IsDefined() and
						(sampler.IsDefined() ? sampler.Get() == right.sampler.Get() : true);
			}
		};

		typedef static_array< BatchTexture, GraphicsConst::MAX_MATERIAL_TEXTURES >	textures_t;
		typedef TOptional< RenderState::ColorStateSeparate >						ColorStateOptional_t;


	// variables
	private:
		ProgramPtr				_program;
		textures_t				_textures;
		ColorStateOptional_t	_colorState;


	// methods
	public:
		BatchMaterial () {}

		BatchMaterial & SetProgram (const ProgramPtr &prog)
		{
			_program = prog;
			return *this;
		}

		BatchMaterial & SetTexture (uint8 stage, const TexturePtr &tex, const SamplerOptional_t &sampler = SamplerOptional_t())
		{
			if ( tex.IsNotNull() ) {
				ASSERT( tex->GetType() == EResource::TEXTURE );
			}
			if ( stage >= _textures.Count() )
			{
				WARNING("");
				return *this;
			}

			_textures[stage].texture = tex;
			_textures[stage].sampler = sampler;
			return *this;
		}

		BatchMaterial & SetColorState (const RenderState::ColorState &state)
		{
			_colorState = RenderState::ColorStateSeparate( state );
			return *this;
		}

		BatchMaterial & SetColorState (const RenderState::ColorStateSeparate &state)
		{
			_colorState = state;
			return *this;
		}

		void ResetColorState ()
		{
			_colorState.Undefine();
		}

		void ResetTextures ()
		{
			_textures = textures_t();
		}

		bool Compare (const BatchMaterial &other) const
		{
			return	_textures == other._textures and
					_program == other._program and (
					( not _colorState.IsDefined() and not other._colorState.IsDefined() ) or
					_colorState == other._colorState );
		}

		bool IsTranslucent () const
		{
			return _colorState ?
					_colorState->blend :
					(_program.IsNotNull() and _program->GetRenderState().colorState.blend);
		}

		void Apply (StateManager *sm) const
		{
			_program->Bind();

			FOR( i, _textures )
			{
				if ( not _program->GetTextures()[i].enabled )
					break;

				if ( _textures[i].texture.IsNull() )
				{
					WARNING( "texture must be defined!" );
					continue;
				}

				_textures[i].texture->Bind( i );

				_textures[i].texture->SetSampler( _textures[i].sampler.IsDefined() ?
												  _textures[i].sampler.Get() :
												  _program->GetTextures()[i].sampler );

				_program->GetTextures()[i].uniform.SetValue( i );
			}

			_program->ApplyRenderState( _colorState );
		}

		void Clear ()
		{
			*this = BatchMaterial();
		}

		bool IsValid () const
		{
			return _program.IsNotNull();
		}

		void SwapMem (BatchMaterial &other)
		{
			_swap( this->_program,		other._program );
			_swap( this->_textures,		other._textures );
			_swap( this->_colorState,	other._colorState );
		}
	};
	

}	// Graphics
}	// Engine